#pragma once
using namespace System;

#define SHOW Console::WriteLine(__FUNCTION__)

#pragma region Class definitions
class NativeClass
{
public:
	int x;
};
struct NativeStruct
{
	int x;
};

ref struct RefStruct
{
	int x;
	RefStruct(){}
	RefStruct(const RefStruct% rs)
	{
		x = rs.x;
	}
};
ref class RefClass
{
public:
	int x;
	RefClass(){}
	RefClass(const RefClass% rc)
	{
		x = rc.x;
	}
};

value struct ValueStruct
{
	int x;
};
value class ValueClass
{
public:
	int x;
};

#pragma endregion

ref class MyTestChange
{
public:
	static void ChangeNativeStruct(NativeStruct ns)
	{
		SHOW;
		ns.x = 10;
	}
	static void ChangeNativeClass(NativeClass nc)
	{
		SHOW;
		nc.x = 10;
	}

	static void ChangeRefStruct(RefStruct rs)
	{
		SHOW;
		rs.x = 10;
	}
	static void ChangeRefClass(RefClass rc)
	{
		SHOW;
		rc.x = 10;
	}

	static void ChangeValueStruct(ValueStruct vs)
	{
		SHOW;
		vs.x = 10;
	}
	static void ChangeValueClass(ValueClass vc)
	{
		SHOW;
		vc.x = 10;
	}

	static void TestChange()
	{
		NativeStruct ns;
		ns.x = 20;
		System::Console::WriteLine("Before Change:{0}",ns.x);
		ChangeNativeStruct(ns);
		System::Console::WriteLine("After Change:{0}",ns.x);

		NativeClass nc;
		nc.x = 20;
		System::Console::WriteLine("Before Change:{0}",nc.x);
		ChangeNativeClass(nc);
		System::Console::WriteLine("After Change:{0}",nc.x);

		RefStruct rs;
		rs.x = 20;
		System::Console::WriteLine("Before Change:{0}",rs.x);
		ChangeRefStruct(rs);
		System::Console::WriteLine("After Change:{0}",rs.x);

		RefClass rc;
		rc.x = 20;
		System::Console::WriteLine("Before Change:{0}",rc.x);
		ChangeRefClass(rc);
		System::Console::WriteLine("After Change:{0}",rc.x);

		ValueStruct vs;
		vs.x = 20;
		System::Console::WriteLine("Before Change:{0}",vs.x);
		ChangeValueStruct(vs);
		System::Console::WriteLine("After Change:{0}",vs.x);

		ValueClass vc;
		vc.x = 20;
		System::Console::WriteLine("Before Change:{0}",vc.x);
		ChangeValueClass(vc);
		System::Console::WriteLine("After Change:{0}",vc.x);
	}

};

ref class MyTestChangeByPointer
{
public:
	static void ChangeNativeStructPointer(NativeStruct* pNs)
	{
		SHOW;
		pNs->x = 10;
	}
	static void ChangeNativeClassPointer(NativeClass* pNc)
	{
		SHOW;
		pNc->x = 10;
	}
	static void ChangeValueStructPointer(ValueStruct* pVs)
	{
		SHOW;
		pVs->x = 10;
	}
	static void ChangeValueClassPointer(ValueClass* pVc)
	{
		SHOW;
		pVc->x = 10;
	}

	static void TestChangeByPointer()
	{
		NativeStruct* pNs = new NativeStruct;
		pNs->x = 20;
		System::Console::WriteLine("Before Change:{0}",pNs->x);
		ChangeNativeStructPointer(pNs);
		System::Console::WriteLine("Before Change:{0}",pNs->x);
		delete pNs;

		NativeClass* pNc = new NativeClass;
		pNc->x = 20;
		System::Console::WriteLine("Before Change:{0}",pNc->x);
		ChangeNativeClassPointer(pNc);
		System::Console::WriteLine("Before Change:{0}",pNc->x);
		delete pNc;

		ValueStruct* pVs = new ValueStruct;
		pVs->x = 20;
		System::Console::WriteLine("Before Change:{0}",pVs->x);
		ChangeValueStructPointer(pVs);
		System::Console::WriteLine("Before Change:{0}",pVs->x);
		delete pVs;

		ValueClass* pVc = new ValueClass;
		pVc->x = 20;
		System::Console::WriteLine("Before Change:{0}",pVc->x);
		ChangeValueClassPointer(pVc);
		System::Console::WriteLine("Before Change:{0}",pVc->x);
		delete pVc;
	}
};

ref class MyTestChangeByHandle
{
public:
	static void ChangeValueStructHandle(ValueStruct^ rVs)
	{
		SHOW;
		rVs->x = 10;
	}
	static void ChangeValueClassHandle(ValueClass^ rVc)
	{
		SHOW;
		rVc->x = 10;
	}
	static void ChangeRefStructHandle(RefStruct^ rRs)
	{		
		SHOW;
		rRs->x = 10;
	}
	static void ChangeRefClassHandle(RefClass^ rRc)
	{		
		SHOW;
		rRc->x = 10;
	}

	static void TestChangeByHandle()
	{
		ValueStruct^ rVs = gcnew ValueStruct;
		rVs->x = 20;
		System::Console::WriteLine("Before Change:{0}",rVs->x);
		ChangeValueStructHandle(rVs);
		System::Console::WriteLine("Before Change:{0}",rVs->x);
		delete rVs;

		ValueClass^ rVc = gcnew ValueClass;
		rVc->x = 20;
		System::Console::WriteLine("Before Change:{0}",rVc->x);
		ChangeValueClassHandle(rVc);
		System::Console::WriteLine("Before Change:{0}",rVc->x);
		delete rVc;

		RefStruct^ rRs = gcnew RefStruct;
		rRs->x = 20;
		System::Console::WriteLine("Before Change:{0}",rRs->x);
		ChangeRefStructHandle(rRs);
		System::Console::WriteLine("Before Change:{0}",rRs->x);
		delete rRs;

		RefClass^ rRc = gcnew RefClass;
		rRc->x = 20;
		System::Console::WriteLine("Before Change:{0}",rRc->x);
		ChangeRefClassHandle(rRc);
		System::Console::WriteLine("Before Change:{0}",rRc->x);
		delete rRc;
	}
};

ref class MyTestChangeByReference
{
public:
	static void ChangeNativeStructReference(NativeStruct& ns)
	{
		SHOW;
		ns.x = 10;
	}
	static void ChangeNativeClassReference(NativeClass& nc)
	{
		SHOW;
		nc.x = 10;
	}
	static void ChangeValueStructReference(ValueStruct& vs)
	{
		SHOW;
		vs.x = 10;
	}
	static void ChangeValueClassReference(ValueClass& vc)
	{
		SHOW;
		vc.x = 10;
	}
	//static void ChangeValueStructReference(ValueStruct% vs)
	//{
	//	SHOW;
	//	vs.x = 10;
	//}
	static void ChangeValueClassReference(ValueClass% vc)
	{
		SHOW;
		vc.x = 10;
	}
	static void ChangeRefStructReference(RefStruct% rs)
	{
		SHOW;
		rs.x = 10;
	}
	static void ChangeRefClassReference(RefClass% rc)
	{
		SHOW;
		rc.x = 10;
	}


	static void TestChangeByReference()
	{
		NativeStruct ns;
		ns.x = 20;
		System::Console::WriteLine("Before Change:{0}",ns.x);
		ChangeNativeStructReference(ns);
		System::Console::WriteLine("After Change:{0}",ns.x);

		NativeClass nc;
		nc.x = 20;
		System::Console::WriteLine("Before Change:{0}",nc.x);
		ChangeNativeClassReference(nc);
		System::Console::WriteLine("After Change:{0}",nc.x);

		ValueStruct vs;
		vs.x = 20;
		System::Console::WriteLine("Before Change:{0}",vs.x);
		ChangeValueStructReference(vs);
		System::Console::WriteLine("After Change:{0}",vs.x);


		//ValueClass vc;
		//vc.x = 20;
		//System::Console::WriteLine("Before Change:{0}",vc.x);
		//ChangeValueStructReference(vc);
		//System::Console::WriteLine("After Change:{0}",vc.x);

		//ValueStruct vs2;
		//vs2.x = 20;
		//System::Console::WriteLine("Before Change:{0}",vs2.x);
		//ChangeValueStructReference(vs2);
		//System::Console::WriteLine("After Change:{0}",vs2.x);

		//ValueClass vc2;
		//vc2.x = 20;
		//System::Console::WriteLine("Before Change:{0}",vc2.x);
		//ChangeValueClassReference(vc2);
		//System::Console::WriteLine("After Change:{0}",vc2.x);

		RefStruct rs;
		rs.x = 20;
		System::Console::WriteLine("Before Change:{0}",rs.x);
		ChangeRefStructReference(rs);
		System::Console::WriteLine("After Change:{0}",rs.x);

		RefClass rc;
		rc.x = 20;
		System::Console::WriteLine("Before Change:{0}",rc.x);
		ChangeRefClassReference(rc);
		System::Console::WriteLine("After Change:{0}",rc.x);
	}
};
ref class MyTestChangeByPointerReference
{
public:
	static void ChangeNativeStructPointerReference(NativeStruct*& pNs)
	{
		SHOW;
		pNs = new NativeStruct;
		pNs->x = 10;
	}	
	static void ChangeNativeClassPointerReference(NativeClass*& pNc)
	{
		SHOW;
		pNc = new NativeClass;
		pNc->x = 10;
	}
	static void ChangeValueStructPointerReference(ValueStruct*& pVs)
	{
		SHOW;
		pVs = new ValueStruct;
		pVs->x = 10;
	}
	static void ChangeValueClassPointerReference(ValueClass*& pVc)
	{
		SHOW;
		pVc = new ValueClass;
		pVc->x = 10;
	}
	static void ChangeValueStructHandleReference(ValueStruct^% rVs)
	{
		SHOW;
		rVs = gcnew ValueStruct;
		rVs->x = 10;
	}
	static void ChangeValueClassHandleReference(ValueClass^% rVc)
	{
		SHOW;
		rVc = gcnew ValueClass;
		rVc->x = 10;
	}
	static void ChangeRefStructHandleReference(RefStruct^% rRs)
	{
		SHOW;
		rRs = gcnew RefStruct;
		rRs->x = 10;
	}
	static void ChangeRefClassHandleReference(RefClass^% rRc)
	{
		SHOW;
		rRc = gcnew RefClass;
		rRc->x = 10;
	}
	static void TestChangeByPointerOrHandleReference()
	{
		NativeStruct* pNs = new NativeStruct;
		pNs->x = 20;
		System::Console::WriteLine("Before Change:{0},{1}",(int)pNs,pNs->x);
		ChangeNativeStructPointerReference(pNs);
		System::Console::WriteLine("After Change:{0},{1}",(int)pNs,pNs->x);
		delete pNs;

		NativeClass* pNc = new NativeClass;
		pNc->x = 20;
		System::Console::WriteLine("Before Change:{0},{1}",(int)pNc,pNc->x);
		ChangeNativeClassPointerReference(pNc);
		System::Console::WriteLine("After Change:{0},{1}",(int)pNc,pNc->x);
		delete pNc;

		ValueStruct* rVs = new ValueStruct;
		rVs->x = 20;
		System::Console::WriteLine("Before Change:{0},{1}",(int)rVs,rVs->x);
		ChangeValueStructPointerReference(rVs);
		System::Console::WriteLine("After Change:{0},{1}",(int)rVs,rVs->x);
		delete rVs;

		ValueClass* rVc = new ValueClass;
		rVc->x = 20;
		System::Console::WriteLine("Before Change:{0},{1}",(int)rVc,rVc->x);
		ChangeValueClassPointerReference(rVc);
		System::Console::WriteLine("After Change:{0},{1}",(int)rVc,rVc->x);
		delete rVc;

		ValueStruct^ rVs2 = gcnew ValueStruct;
		rVs2->x = 20;
		System::Console::WriteLine("Before Change:{0},{1}",rVs2,rVs2->x);
		ChangeValueStructHandleReference(rVs2);
		System::Console::WriteLine("After Change:{0},{1}",rVs2,rVs2->x);
		delete rVs2;

		ValueClass^ rVc2 = gcnew ValueClass;
		rVc2->x = 20;
		System::Console::WriteLine("Before Change:{0},{1}",rVc2,rVc2->x);
		ChangeValueClassHandleReference(rVc2);
		System::Console::WriteLine("After Change:{0},{1}",rVc2,rVc2->x);
		delete rVc2;

		RefStruct^ rRs = gcnew RefStruct;
		rRs->x = 20;
		System::Console::WriteLine("Before Change:{0},{1}",rRs,rRs->x);
		ChangeRefStructHandleReference(rRs);
		System::Console::WriteLine("After Change:{0},{1}",rRs,rRs->x);
		delete rRs;

		RefClass^ rRc = gcnew RefClass;
		rRc->x = 20;
		System::Console::WriteLine("Before Change:{0},{1}",rRc,rRc->x);
		ChangeRefClassHandleReference(rRc);
		System::Console::WriteLine("After Change:{0},{1}",rRc,rRc->x);
		delete rRc;

	}
};

void Test()
{
	//MyTestChange::TestChange();
	//MyTestChangeByPointer::TestChangeByPointer();
	//MyTestChangeByHandle::TestChangeByHandle();
	//MyTestChangeByReference::TestChangeByReference();
	//MyTestChangeByPointerReference::TestChangeByPointerOrHandleReference();

}